

class Tick(object):
    """
    Element For Ramp Widget Basic U and V Attribute holder
    斜坡小部件的基本元素，用于保存 U 和 V 属性。
    """

    def __init__(self):
        # 初始化 U 属性，默认为 0
        self._u = 0
        # 初始化 V 属性，默认为 0
        self._v = 0
        # 初始化选中状态，默认为未选中
        self._selected = False

    def getU(self):
        """
        获取 U 属性的值。

        :return: U 属性的值
        """
        return self._u

    def getV(self):
        """
        获取 V 属性的值。

        :return: V 属性的值
        """
        return self._v

    def setU(self, u):
        """
        设置 U 属性的值。

        :param u: 要设置的 U 属性的值
        """
        self._u = u

    def setV(self, v):
        """
        设置 V 属性的值。

        :param v: 要设置的 V 属性的值
        """
        self._v = v

    def setSelected(self, selected):
        """
        设置元素的选中状态。

        :param selected: 布尔值，True 表示选中，False 表示未选中
        """
        self._selected = selected

    def isSelected(self):
        """
        判断元素是否被选中。

        :return: 布尔值，True 表示选中，False 表示未选中
        """
        return self._selected


class splineRamp(object):
    """
    Ramp/Curve Editor with evaluateAt support , clamped to 0-1 in both x and y
    斜坡/曲线编辑器，支持在指定值处进行评估，x 和 y 的值被限制在 0 到 1 之间。
    """

    def __init__(self):
        # 初始化存储 Tick 元素的列表
        self.items = []

    def __getitem__(self, index):
        """
        根据索引获取排序后的 Tick 元素。

        :param index: 要获取元素的索引
        :return: 如果索引有效则返回对应的 Tick 元素，否则返回 None
        """
        if len(self.items) and index in range(0, len(self.items)):
            return self.sortedItems()[index]
        else:
            return None

    @property
    def uValues(self):
        """
        获取所有 Tick 元素的 U 属性值列表。

        :return: 包含所有 Tick 元素 U 属性值的列表
        """
        return [x.getU() for x in self.sortedItems()]

    @property
    def yValues(self):
        """
        获取所有 Tick 元素的 V 属性值列表。

        :return: 包含所有 Tick 元素 V 属性值的列表
        """
        return [x.getV() for x in self.sortedItems()]

    def sortedItems(self):
        """
        对存储的 Tick 元素按照 U 属性值进行排序。

        :return: 排序后的 Tick 元素列表
        """
        itms = list(self.items)
        itms.sort(key=lambda x: x.getU())
        return itms

    def clear(self):
        """
        清空存储的所有 Tick 元素。
        """
        self.items = []

    def addItem(self, u, v):
        """
        添加一个新的 Tick 元素到列表中。

        :param u: 新 Tick 元素的 U 属性值
        :param v: 新 Tick 元素的 V 属性值
        :return: 新创建的 Tick 元素
        """
        item = Tick()
        item.setU(u)
        item.setV(v)
        self.items.append(item)
        return item

    def removeItem(self, item=None, index=-1):
        """
        从列表中移除指定的 Tick 元素。

        :param item: 要移除的 Tick 元素对象，默认为 None
        :param index: 要移除元素的索引，默认为 -1
        """
        if item:
            if item in self.items:
                self.items.remove(item)
        elif index in range(0, len(self.items) - 1):
            self.items.remove(self.items[index])

    def setU(self, u, index=-1):
        """
        设置指定索引的 Tick 元素的 U 属性值。

        :param u: 要设置的 U 属性值
        :param index: 要设置元素的索引，默认为 -1
        """
        if index in range(0, len(self.items) - 1):
            self.sortedItems()[index].setU(u)

    def setV(self, v, index=-1):
        """
        设置指定索引的 Tick 元素的 V 属性值。

        :param v: 要设置的 V 属性值
        :param index: 要设置元素的索引，默认为 -1
        """
        if index in range(0, len(self.items) - 1):
            self.sortedItems()[index].setV(v)

    def evaluateAt(self, value, bezier=False):
        """
        在指定值处评估斜坡/曲线的值。

        :param value: 要评估的 x 值
        :param bezier: 是否使用贝塞尔插值，默认为 False
        :return: 评估得到的 y 值
        """
        items = self.sortedItems()
        if len(items) > 1:
            if value >= items[-1].getU():
                return items[-1].getV()
            elif value <= items[0].getU():
                return items[0].getV()

            if bezier:
                if isinstance(items[0].getV(), list):
                    v = []
                    for i in range(len(items[0].getV())):
                        v.append(
                            self.interpolateBezier(
                                [p.getV()[i] for p in items], 0, len(items) - 1, value
                            )
                        )
                else:
                    v = self.interpolateBezier(
                        [p.getV() for p in items], 0, len(items) - 1, value
                    )
            else:
                interval = len(items) - 1
                for i, x in enumerate(items):
                    if value <= x.getU():
                        interval = i
                        break

                u = max(
                    0,
                    min(
                        1,
                        (
                            ((value - items[interval - 1].getU()) * (1.0 - 0.0))
                            / (items[interval].getU() - items[interval - 1].getU())
                        )
                        + 0.0,
                    ),
                )

                start = items[interval].getV()
                end = items[interval - 1].getV()
                if (
                    isinstance(start, list)
                    and isinstance(end, list)
                    and len(start) == len(end)
                ):
                    v = []
                    for i, element in enumerate(start):
                        v.append(self.interpolateLinear(start[i], end[i], u))
                elif not isinstance(start, list) or not isinstance(end, list):
                    v = self.interpolateLinear(start, end, u)

            return v
        elif len(items) == 1:
            return items[0].getV()
        else:
            return 0.0

    def interpolateBezier(self, coorArr, i, j, t):
        """
        执行贝塞尔插值。

        :param coorArr: 坐标数组
        :param i: 起始索引
        :param j: 结束索引
        :param t: 插值参数
        :return: 插值结果
        """
        if j == 0:
            return coorArr[i]
        return (
            self.interpolateBezier(coorArr, i, j - 1, t) * (1 - t)
            + self.interpolateBezier(coorArr, i + 1, j - 1, t) * t
        )

    @staticmethod
    def interpolateLinear(start, end, ratio):
        """
        执行线性插值。

        :param start: 起始值
        :param end: 结束值
        :param ratio: 插值比例
        :return: 插值结果
        """
        return ratio * start + (1 - ratio) * end


if __name__ == "__main__":
    # 创建一个 splineRamp 实例
    ramp = splineRamp()
    # 向实例中添加两个 Tick 元素
    ramp.addItem(0.1, [0.0, 0.0, 0.0])
    ramp.addItem(1.0, [1.0, 1.0, 1.0])

    # 在 0.5 处使用贝塞尔插值评估斜坡的值并打印结果
    print(ramp.evaluateAt(0.5, bezier=True))